package com.ticket.sass.admin.business.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ticket.sass.admin.business.OrderCommoditiesBusiness;
import com.ticket.sass.admin.dto.input.order.DispatchInput;
import com.ticket.sass.admin.dto.input.order.OrderCommodityListInput;
import com.ticket.sass.admin.dto.input.order.PrintInput;
import com.ticket.sass.admin.dto.input.order.RefundInput;
import com.ticket.sass.admin.dto.output.order.CommodityOutput;
import com.ticket.sass.admin.dto.output.order.CouponOutput;
import com.ticket.sass.admin.dto.output.order.OrderCommodityListOutput;
import com.ticket.sass.admin.dto.output.order.OrderDataModel;
import com.ticket.sass.admin.manager.OrderCommodityManager;
import com.ticket.sass.admin.manager.OrderManager;
import com.ticket.sass.admin.manager.OrderTicketManager;
import com.ticket.sass.admin.manager.wx.WxaMessageManager;
import com.ticket.sass.admin.model.KeyInfoModel;
import com.ticket.sass.admin.service.impl.*;
import com.ticket.sass.admin.util.EscapeUtil;
import com.ticket.sass.admin.util.ModelUtil;
import com.ticket.sass.common.entity.tenant.*;
import com.ticket.sass.common.entity.tenant.enums.*;
import com.ticket.sass.common.exception.enums.ErrorCode;
import com.ticket.sass.common.exception.exception.BadRequestException;
import com.ticket.sass.common.printer.config.YiLianYunPrinterConfig;
import com.ticket.sass.common.printer.dto.AccessTokenResult;
import com.ticket.sass.common.printer.enums.PrinterType;
import com.ticket.sass.common.printer.factory.PrinterServiceFactory;
import com.ticket.sass.common.printer.service.PrinterService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author ywb
 * @date 2024-03-14 14:23
 */
@Slf4j
@Service
@AllArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class OrderCommoditiesBusinessImpl implements OrderCommoditiesBusiness {
    final ModelUtil modelUtil;
    final OrderManager orderManager;
    final UserServiceImpl userService;
    final OrderServiceImpl orderService;
    final CinemaServiceImpl cinemaService;
    final UserWxServiceImpl userWxService;
    final OrderCommodityServiceImpl service;
    final WxaMessageManager wxMaMessageManager;
    final OrderTicketManager orderTicketManager;
    final CommodityServiceImpl commodityService;
    final MemberCardServiceImpl memberCardService;
    final OrderTicketServiceImpl orderTicketService;
    final FilmScheduleServiceImpl filmScheduleService;
    final OrderCommodityManager orderCommodityManager;
    final OrderCommodityDetailServiceImpl detailService;
    final OrderCouponMapServiceImpl orderCouponMapService;
    final CommodityCloudPrinterServiceImpl commodityCloudPrinterService;
    final OrderCommodityDispatchServiceImpl orderCommodityDispatchService;


    @Override
    public OrderDataModel<OrderCommodityListOutput> list(OrderCommodityListInput input) {
        OrderDataModel orderDataModel = new OrderDataModel();
        List<Long> selectUserIds = userService.getUserIdByCellphone(input.getCellphone());
        if (StrUtil.isNotEmpty(input.getCellphone()) && selectUserIds.isEmpty()) {
            return orderDataModel;
        }
        input.setUserIds(selectUserIds);
        
        IPage<OrderCommodityEntity> page = service.iPage(input);
        List<OrderCommodityListOutput> outputs = convertToOutputList(page.getRecords());
        
        Map<Long, List<OrderCommodityDetailEntity>> orderCommodityDetailsByOrderIds = detailService.findOrderCommodityDetailsByOrderIds(getOrderCommodityIds(outputs));
        
        List<Long> orderIds = getOrderIds(outputs);
        
        Map<Long, OrderEntity> longOrderEntityMap = orderService.mapByIds(orderIds);
        
        Map<Long, List<OrderCouponMapEntity>> orderCouponMapByOrderIds = orderCouponMapService.getOrderCouponMapByOrderIds(orderIds);
        
        Map<Long, OrderCommodityDispatchEntity> orderCommodityDispatchMapByOrderIds = orderCommodityDispatchService.getOrderCommodityDispatchMapByOrderIds(orderIds);
        
        populateOutputsWithDetailsAndCoupons(outputs, orderCommodityDetailsByOrderIds, orderCouponMapByOrderIds, orderCommodityDispatchMapByOrderIds, longOrderEntityMap);
        
        orderDataModel.setTotal(page.getTotal());
        orderDataModel.setList(outputs);
        
        List<Long> longs = service.orderIds(input);
        
        OrderEntity order = orderService.dateCount(OrderType.COMMODITY.getValue(), null, longs);
        
        OrderEntity refundOrder = orderService.dateCount(OrderType.COMMODITY.getValue(), OrderPayStatus.REFUND_SUCCESS.getValue(), longs);
        
        if (Objects.nonNull(order)){
            orderDataModel.setSumPayPrice(order.getSumPayPrice());
            orderDataModel.setWxSumPayPrice(order.getWxSumPayPrice());
            orderDataModel.setMemberSumPayPrice(order.getMemberSumPayPrice());
            orderDataModel.setCount(order.getTotalRecords());
        }
        
        if (Objects.nonNull(refundOrder)){
            orderDataModel.setRefundAmount(refundOrder.getSumPayPrice());
            orderDataModel.setRefundCount(refundOrder.getTotalRecords());
            
            orderDataModel.setRefundWxAmount(refundOrder.getWxSumPayPrice());
            orderDataModel.setRefundMemberAmount(refundOrder.getMemberSumPayPrice());
        }
        return orderDataModel;
    }
    
    private List<Long> getOrderCommodityIds(List<OrderCommodityListOutput> outputs) {
        return outputs.stream()
            .map(OrderCommodityListOutput::getId)
            .collect(Collectors.toList());
    }
    
    private List<Long> getOrderIds(List<OrderCommodityListOutput> outputs) {
        return outputs.stream()
            .map(OrderCommodityListOutput::getOrderId)
            .collect(Collectors.toList());
    }
    
    private List<OrderCommodityListOutput> convertToOutputList(List<OrderCommodityEntity> entities) {
        return entities.stream()
            .map(OrderCommodityListOutput::new)
            .collect(Collectors.toList());
    }
    
    private void populateOutputsWithDetailsAndCoupons(List<OrderCommodityListOutput> outputs,
                                                      Map<Long, List<OrderCommodityDetailEntity>> orderCommodityDetailsByOrderIds,
                                                      Map<Long, List<OrderCouponMapEntity>> orderCouponMapByOrderIds,
                                                      Map<Long, OrderCommodityDispatchEntity> orderCommodityDispatchEntityMap,
                                                      Map<Long, OrderEntity> orderEntityMap
    ) {
        outputs.forEach(o -> {
            UserEntity userEntity = getUserEntity(o);
            o.setUsername(userEntity.getNickname());
            o.setCellphone(userEntity.getCellphone());
            
            List<OrderCommodityDetailEntity> orderCommodityDetailEntities = orderCommodityDetailsByOrderIds.getOrDefault(o.getId(), new ArrayList<>());
            o.setCommodities(getCommodityOutputs(orderCommodityDetailEntities));
            
            if (o.getUseCoupon()) {
                List<OrderCouponMapEntity> couponMapEntities = orderCouponMapByOrderIds.getOrDefault(o.getOrderId(), new ArrayList<>());
                o.setCoupons(getCouponOutputs(couponMapEntities));
            } else {
                o.setCoupons(new ArrayList<>());
            }
            
            OrderCommodityDispatchEntity dispatch = orderCommodityDispatchEntityMap.getOrDefault(o.getOrderId(), null);
            o.setDispatchInfo(dispatch);
            
            OrderEntity orDefault = orderEntityMap.getOrDefault(o.getOrderId(), new OrderEntity());
            o.setRefundRemark(orDefault.getRefundRemark());
            
            
        });
    }
    
    private UserEntity getUserEntity(OrderCommodityListOutput o) {
        return userService.getById(o.getUserId());
    }
    
    private List<CommodityOutput> getCommodityOutputs(List<OrderCommodityDetailEntity> orderCommodityDetailEntities) {
        return orderCommodityDetailEntities.stream()
            .map(this::mapToCommodityOutput)
            .collect(Collectors.toList());
    }
    
    private CommodityOutput mapToCommodityOutput(OrderCommodityDetailEntity detailEntity) {
        CommodityEntity commodityEntity = commodityService.getById(detailEntity.getCommodityId());
        CommodityOutput out = new CommodityOutput();
        out.setSkuInfo(detailEntity.getSkuInfo());
        out.setPrice(detailEntity.getPrice());
        out.setNum(detailEntity.getNum());
        out.setCover(KeyInfoModel.generationList(commodityEntity.getCover()));
        out.setName(commodityEntity.getName());
        return out;
    }
    
    private List<CouponOutput> getCouponOutputs(List<OrderCouponMapEntity> couponMapEntities) {
        return couponMapEntities.stream()
            .map(this::mapToCouponOutput)
            .collect(Collectors.toList());
    }
    
    private CouponOutput mapToCouponOutput(OrderCouponMapEntity couponMapEntity) {
        return new CouponOutput(couponMapEntity.getCouponId(), couponMapEntity.getCouponName(), couponMapEntity.getDiscountAmount());
    }
    
    @Override
    public void print(PrintInput input) {
        validateOrderId(input.getOrderId());
        
        CommodityCloudPrinterEntity printer = commodityCloudPrinterService.getById(input.getPrinterId());
        if (Objects.isNull(printer)){
            throw new BadRequestException(
                ErrorCode.RECORD_NOT_EXIST,
                EscapeUtil.errorMessage("printerId", "打印机不存在")
            );
        }
        
        Long orderId = input.getOrderId();
        OrderEntity order = orderService.getById(orderId);
        OrderCommodityEntity orderCommodity = service.getOneByOrderId(orderId);
        OrderCommodityDispatchEntity orderCommodityDispatch = orderCommodityDispatchService.getOneByOrderId(orderId);
        CinemaEntity cinemaEntity = cinemaService.getById(order.getCinemaId());
        UserEntity user = userService.getById(order.getUserId());
        List<CommodityOutput> commodityOutputs = getCommodityOutputs(detailService.findOrderCommodityDetailsByOrderId(orderCommodity.getId()));
        
        String content = generatePrintContent(cinemaEntity, orderCommodity, user, order, orderCommodityDispatch, commodityOutputs);
        
        YiLianYunPrinterConfig config = YiLianYunPrinterConfig.fromJson(printer.getData());
        PrinterService printerService = PrinterServiceFactory.getPrinterService(PrinterType.YI_LIAN_YUN);
        try {
            printerService.setConfig(config);
            AccessTokenResult print = printerService.print(content);
            System.out.println(print);
        } catch (Exception ex){
            ex.printStackTrace();
            throw new BadRequestException(
                ErrorCode.RECORD_NOT_EXIST,
                EscapeUtil.errorMessage("printerId", "打印机不可用！")
            );
        }
        
        orderCommodity.setPrintNum(orderCommodity.getPrintNum() + 1);
        service.updateById(orderCommodity);
    }
    
    private String generatePrintContent(CinemaEntity cinemaEntity, OrderCommodityEntity orderCommodity, UserEntity user,
                                        OrderEntity order, OrderCommodityDispatchEntity orderCommodityDispatch,
                                        List<CommodityOutput> commodityOutputs) {
        String dispatchInfo = "";
        if (Objects.nonNull(orderCommodityDispatch)){
            // 没有关联的影票订单
            if (!Objects.equals(orderCommodityDispatch.getOrderIdAsTicket(), 0L)){
                OrderTicketEntity orderTicket = orderTicketService.getOneByOrderId(orderCommodityDispatch.getOrderIdAsTicket());
                FilmScheduleEntity filmSchedule = filmScheduleService.getById(orderTicket.getFilmScheduleId());
                if (filmSchedule != null){
                    dispatchInfo = String.format("影片：%s\n放映：%s", filmSchedule.getFilmName(), filmSchedule.getStartTime());
                }
            }
        }
        
        String dispatchMode = determineDispatchMode(orderCommodityDispatch);
        String dispatchPlace = orderCommodityDispatch != null ? orderCommodityDispatch.getPlace() : "";
        String maskedPhone = user.getCellphone() != null ? masked(user.getCellphone()) : "暂无";
        String commodityInfo = commodityInfoFormat(commodityOutputs);
        String totalPrice = formatTotalPrice(order.getPayPrice());
        
        TemplateAttr data = new TemplateAttr(
            cinemaEntity.getName(),
            LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")),
            orderCommodity.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH:mm:ss")),
            orderCommodity.getTakeCode(),
            dispatchInfo,
            dispatchMode,
            dispatchPlace,
            maskedPhone,
            order.getOrderNo(),
            commodityInfo,
            totalPrice
        );
        
        String template =
                "<center><FS>%s</FS></center>\n\n" +
                "<center><FS>取餐码：%s</FS></center>\n" +
                "<center>%s</center>\n\n" +
                "%s\n" +
                "电话：%s\n" +
                "单号：%s\n" +
                "下单：%s\n\n" +
                "%s %s\n" +
                "--------------------------------\n" +
                "%s\n" +
                "--------------------------------\n\n" +
                "<FB>合计：%s元</FB>\n\n" +
                "<center>---- 向荣影管提供技术服务 ----</center>\n\n";
        
        return data.format(template);
    }
    
    private static String formatTotalPrice(BigDecimal payPrice) {
        String totalPrice = payPrice.stripTrailingZeros().toPlainString();
        int totalLength = 32; // Total length for the formatted line
        int priceLength = totalPrice.length();
        int paddingLength = totalLength - 8 - priceLength; // Subtract 8 for "合计：" and "元" characters
        String padding = " ".repeat(Math.max(0, paddingLength));
        return padding + totalPrice;
    }
    
    private String determineDispatchMode(OrderCommodityDispatchEntity orderCommodityDispatch) {
        if (orderCommodityDispatch == null) {
            return "前台自提";
        }
        CommodityDispatchMode mode = orderCommodityDispatch.getMode();
        switch (mode) {
            case HALL:
                return "配送到影厅";
            case SEAT:
                return "配送入座";
            case NONSUPPORT:
            case TAKE:
            default:
                return "前台自提";
        }
    }
    
    class TemplateAttr {
        private String cinemaName;
        private String date;
        private String takeCode;
        private String dispatchInfo;
        private String dispatchMode;
        private String dispatchPlace;
        private String phone;
        private String orderNo;
        private String commodityInfo;
        private String totalPrice;
        private String createTime;
        
        public TemplateAttr(String cinemaName, String date, String createTime, String takeCode, String dispatchInfo,
                            String dispatchMode, String dispatchPlace, String phone,
                            String orderNo, String commodityInfo, String totalPrice) {
            this.cinemaName = cinemaName;
            this.date = date;
            this.createTime = createTime;
            this.takeCode = takeCode;
            this.dispatchInfo = dispatchInfo;
            this.dispatchMode = dispatchMode;
            this.dispatchPlace = dispatchPlace;
            this.phone = phone;
            this.orderNo = orderNo;
            this.commodityInfo = commodityInfo;
            this.totalPrice = totalPrice;
        }
        
        public String format(String template) {
            return String.format(template, cinemaName, takeCode, date, dispatchInfo, phone, orderNo, createTime, dispatchMode, dispatchPlace, commodityInfo, totalPrice);
        }
    }
    
    
    public static String masked(String input) {
        if (input == null || input.length() < 7) {
            return input; // 处理长度不足的情况
        }
        return input.substring(0, 3) + "****" + input.substring(input.length() - 4);
    }
    
    // 获取尾号
    public static String getTailNumber(String input) {
        if (input == null || input.length() < 4) {
            return input; // 处理长度不足的情况
        }
        return input.substring(input.length() - 4);
    }
    
    private static String commodityInfoFormat(List<CommodityOutput> commodityOutputs) {
        return commodityOutputs.stream()
            .map(detail -> {
                String price = detail.getPrice().stripTrailingZeros().toPlainString();
                int nameLength = count(detail.getName());
                int skuInfoLength = count(detail.getSkuInfo());
                int priceLength = count(price);
                int blankLength = 26 - (nameLength + skuInfoLength + priceLength);
                // 使用Math.max来确保blankLength不会为负数
                String blank = " ".repeat(Math.max(0, blankLength));
                
                return String.format("%s %s%sx%d %s元",
                    detail.getName(),
                    detail.getSkuInfo(),
                    blank,
                    detail.getNum(),
                    price);
            })
            .collect(Collectors.joining("\n"));
    }
    
    private static int count(String input) {
        int count = 0;
        for (char c : input.toCharArray()) {
            // 使用正则表达式判断是否为汉字字符
            if (String.valueOf(c).matches("[\\u0391-\\uFFE5]")) {
                count += 2;
            } else {
                count += 1;
            }
        }
        return count;
    }

    @Override
    public void dispatch(DispatchInput input) {
        validateOrderId(input.getOrderId());
        OrderCommodityEntity orderCommodity = service.getOneByOrderId(input.getOrderId());
        if (orderCommodity.getStatus() == OrderCommodityStatus.REFUND_ING || orderCommodity.getStatus() == OrderCommodityStatus.REFUND_SUCCESS){
            throw new BadRequestException(
                ErrorCode.RECORD_NOT_EXIST,
                EscapeUtil.errorMessage("orderId", "订单已退")
            );
        }
      
      
        OrderCommodityDispatchEntity oneByOrderId = orderCommodityDispatchService.getOneByOrderId(input.getOrderId());
        if (oneByOrderId.getStatus() == CommodityDispatchStatus.DELIVERED){
            throw new BadRequestException(
                ErrorCode.RECORD_NOT_EXIST,
                EscapeUtil.errorMessage("orderId", "订单已配送")
            );
        }
        orderCommodityDispatchService.update(new UpdateWrapper<OrderCommodityDispatchEntity>().set("status", 1).eq("order_id_as_commodity", input.getOrderId()));
        
        String openid = userWxService.getOpenidByUserId(orderCommodity.getUserId());
        
        
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm");

        // 格式化 LocalDateTime.now() 和 oneByOrderId.getCreatedAt()
        String createdAtFormatted = oneByOrderId.getCreatedAt().format(formatter);
        
        List<CommodityOutput> commodityOutputs = getCommodityOutputs(detailService.findOrderCommodityDetailsByOrderId(orderCommodity.getId()));
        // 调用 wxMaMessageManager.orderCommodityDispatch 方法
        wxMaMessageManager.orderCommodityDispatch(
            input.getOrderId(),
            oneByOrderId.getCinemaId(),
            openid,
            new WxaMessageManager.OrderCommodityDispatchTpl(
                orderCommodity.getTakeCode(),
//                "激光厅激光厅激光厅",
                    oneByOrderId.getPlace().substring(0, Math.min(5, oneByOrderId.getPlace().length())),
                commodityOutputs.stream()
                    .map(CommodityOutput::getName)
                    .collect(Collectors.joining(" + ")),
                createdAtFormatted,
                "您的订单已准备好，请前往取餐区取餐"
            )
        );
        
    }

    

    @Override
    public void refund(RefundInput input) {
        // 校验订单ID
        validateOrderId(input.getOrderId());
            
        OrderEntity order = orderService.getById(input.getOrderId());
        
        // 混合订单
        if (order.getOrderMixedId() != 0){
            List<OrderEntity> orderEntities = orderService.listByInMixedId(order.getOrderMixedId());
            orderEntities.forEach(it -> {
                it.setRefundRemark(input.getRemark());
                if (it.getType() == OrderType.TICKET){
                    orderTicketManager.refund(it, OrderTicketManager.RefundType.TICKET_AND_REFUND, input.getRefundCoupon());
                } else {
                    orderCommodityManager.refund(it, input.getRefundCoupon());
                }
            });
            
            OrderMixedEntity orderMixed = orderManager.updateOrderMixedStatus(order.getOrderMixedId());
            
            // 如果是微信支付
            if (order.getPayType() == OrderPayType.WXA){
                orderManager.refundToWeChat(order, orderMixed);
            }
        // 单独订单
        } else {
            order.setRefundRemark(input.getRemark());
            orderCommodityManager.refund(order, input.getRefundCoupon());
            
            // 如果是微信支付
            if (order.getPayType() == OrderPayType.WXA){
                orderManager.refundToWeChat(order, null);
            }
        }
    }
    
    private void validateOrderId(Long id) {
        if (orderService.isEmpty(id)) {
            throw new BadRequestException(
                ErrorCode.RECORD_NOT_EXIST,
                ErrorCode.RECORD_NOT_EXIST.getReasonPhrase()
            );
        }
    }
    
}
